Dans ce notebook, nous allons comparer différents modèles de reconnaissance de tumeurs cérébrales à l'aide des données de test. Les objectifs sont :
La precision mesure la proportion des prédictions positives correctes pour une classe donnée par rapport à toutes les prédictions positives faites pour cette classe :
$$ \text{Precision}_i = \frac{\text{VP}_i}{\text{VP}_i + \text{FP}_i} $$Le recall mesure la proportion des échantillons réellement appartenant à une classe qui ont été correctement identifiés :
$$ \text{Recall}_i = \frac{\text{VP}_i}{\text{VP}_i + \text{FN}_i} $$Le $F_1$-score combine precision et recall dans une moyenne harmonique. Il équilibre les deux métriques, particulièrement utile en cas de données déséquilibrées :
$$ F_{1,i} = 2 \cdot \frac{\text{Precision}_i \cdot \text{Recall}_i}{\text{Precision}_i + \text{Recall}_i} $$Pour comparer les modèles de manière globale, nous définissons une métrique agrégée qui combine les scores de precision, recall et $F_1$ pour toutes les classes. Cela est réalisé via une moyenne pondérée, tenant compte du nombre d'échantillons dans chaque classe.
Precision globale (moyenne pondérée) : $$ \text{Precision Global} = \frac{\sum_{i=1}^{4} n_i \cdot \text{Precision}_i}{\sum_{i=1}^{4} n_i} $$
Recall global (moyenne pondérée) : $$ \text{Recall Global} = \frac{\sum_{i=1}^{4} n_i \cdot \text{Recall}_i}{\sum_{i=1}^{4} n_i} $$
$F_1$-score global (basé sur les totals agrégés pour toutes les classes) : $$ F_1\text{-score Global} = 2 \cdot \frac{\text{Precision Global} \cdot \text{Recall Global}}{\text{Precision Global} + \text{Recall Global}} $$
où :
Évaluation des modèles individuellement pour chaque classe :
Calcul des métriques globales :
Visualisation des performances :
Tester différentes approches pour gérer les déséquilibres dans les données, telles que :
Analyser l'effet de ces approches sur les metrics globales et individuelles.
À la fin de ce notebook, nous serons en mesure de :
import os
import yaml
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
from sklearn.metrics import (
confusion_matrix, classification_report, roc_curve, auc
)
from sklearn.preprocessing import label_binarize
from module_for_preprocessing_metrics import *
with open('../config.yml', 'r') as file:
config = yaml.safe_load(file)
size = config['données']['image']['size']
Dans cette section, nous allons charger et explorer nos données de test. Ces données seront utilisées pour évaluer les performances des modèles entraînés.
De plus, nous accédons également aux historiques des modèles (history), qui contiennent des informations sur l'évolution des métriques telles que la précision (accuracy) et la perte (loss) pendant l'entraînement. Ces historiques seront utilisés pour visualiser les performances des modèles au fil des époques.
testing = os.path.join(os.path.abspath(os.path.join(os.getcwd(), "..")), "data", "Testing")
x_test, y_test = load_images_with_preprocessing(testing, size)
models_history = os.path.join(os.path.join("..", "src", "models", "sauvegardes_modeles"), 'models_history.json')
Ce glossaire décrit les modèles évalués dans le cadre de notre benchmark. Chaque modèle présente une architecture ou une stratégie d'entraînement différente pour comparer leurs performances sur la reconnaissance de tumeurs cérébrales.
| Modèle | Architecture | Augmentation de données | Activation des convolutions |
|---|---|---|---|
| Baseline | Modèle préentraîné Inception optimisé pour le domaine médical | Non | N/A |
| Model 1 | Sequential avec 4 convolutions (ReLU), 2 max pooling, 1 fully connected (ReLU), Dropout (20%) | Non | ReLU |
| Model 2 | Sequential identique au Model 1 | Oui (équilibrage, zoom, décalage) | ReLU |
| Model 3 | Sequential avec 4 convolutions (sigmoid), 2 max pooling, 1 fully connected (ReLU), Dropout (20%) | Non | Sigmoid |
#baseline = load_saved_model('inception1.h5')
model_1 = load_saved_model('modele_brain_tumor_20241117_205951.h5')
model_2 = load_saved_model('modele_brain_tumor_20241125_002219.h5')
model_3 = load_saved_model('modele_brain_tumor_20241210_123938.h5')
plot_confusion_matrix(model_1,x_test,y_test)
13/13 [==============================] - 33s 3s/step
Classification Report:
precision recall f1-score support
Glioma 0.81 0.17 0.28 100
Meningioma 0.68 0.88 0.77 115
No Tumor 0.58 0.96 0.73 105
Pituitary 0.88 0.61 0.72 74
accuracy 0.67 394
macro avg 0.74 0.65 0.62 394
weighted avg 0.72 0.67 0.62 394
courbe_ROC(model_1,x_test,y_test)
13/13 [==============================] - 28s 2s/step
La matrice de confusion révèle les performances du modèle sur chaque classe :
Glioma :
Meningioma :
No Tumor :
Pituitary :
Les courbes ROC et les scores AUC permettent de visualiser les performances globales du modèle pour chaque classe.
Glioma : AUC = 0.57
Meningioma : AUC = 0.91
No Tumor : AUC = 0.94
Pituitary : AUC = 0.94
plot_confusion_matrix(model_2,x_test,y_test)
13/13 [==============================] - 29s 2s/step
Classification Report:
precision recall f1-score support
Glioma 0.77 0.17 0.28 100
Meningioma 0.69 0.84 0.76 115
No Tumor 0.56 0.92 0.70 105
Pituitary 0.88 0.69 0.77 74
accuracy 0.66 394
macro avg 0.73 0.66 0.63 394
weighted avg 0.71 0.66 0.62 394
courbe_ROC(model_2,x_test,y_test)
13/13 [==============================] - 29s 2s/step
La matrice de confusion révèle les performances du modèle sur chaque classe :
Glioma :
Meningioma :
No Tumor :
Pituitary :
Les courbes ROC et les scores AUC permettent de visualiser les performances globales du modèle pour chaque classe.
Glioma : AUC = 0.53
Meningioma : AUC = 0.89
No Tumor : AUC = 0.90
Pituitary : AUC = 0.93
plot_confusion_matrix(model_3,x_test,y_test)
13/13 [==============================] - 31s 2s/step
Classification Report:
precision recall f1-score support
Glioma 0.25 1.00 0.40 100
Meningioma 0.00 0.00 0.00 115
No Tumor 0.00 0.00 0.00 105
Pituitary 0.00 0.00 0.00 74
accuracy 0.25 394
macro avg 0.06 0.25 0.10 394
weighted avg 0.06 0.25 0.10 394
/Users/lilya/anaconda3/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1469: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior. /Users/lilya/anaconda3/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1469: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior. /Users/lilya/anaconda3/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1469: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.
courbe_ROC(model_3,x_test,y_test)
13/13 [==============================] - 30s 2s/step
La matrice de confusion révèle les performances du modèle sur chaque classe :
Glioma :
Meningioma :
No Tumor :
Pituitary :
Les courbes ROC et les scores AUC permettent de visualiser les performances globales du modèle pour chaque classe.
Glioma : AUC = 0.57
Meningioma : AUC = 0.51
No Tumor : AUC = 0.35
Pituitary : AUC = 0.23
Les résultats obtenus sont très mauvais, à tel point qu'une prédiction aléatoire serait plus efficace.
Cela peut s'expliquer par une saturation de la fonction sigmoid, qui limite la capacité du modèle à apprendre des données de manière efficace.
L'ImageDataGenerator est une classe de la bibliothèque Keras utilisée pour générer de nouvelles images à partir des données existantes. Elle applique des transformations comme des rotations, des zooms ou des décalages afin d'augmenter artificiellement la taille et la diversité du jeu de données.
Pour équilibrer les classes et améliorer la robustesse du modèle, nous avons utilisé les transformations suivantes :
Ces techniques permettent d'augmenter la diversité des données et de réduire le surapprentissage, contribuant ainsi à des performances globales améliorées.
plot_model_curves(models_history, 'modele_brain_tumor_20241117_205951')
plot_model_curves(models_history, 'modele_brain_tumor_20241125_002219')
Les courbes de loss et d'accuracy semblent similaires pour les deux modèles étudiés. Afin d'approfondir notre comparaison, analysons leurs performances sur le jeu de test.
print('Test Accuracy du modele 1')
print('')
print(get_test_accuracy(models_history, 'modele_brain_tumor_20241117_205951'))
print('')
print('Test Accuracy du modele 2')
print('')
print(get_test_accuracy(models_history, 'modele_brain_tumor_20241125_002219'))
Test Accuracy du modele 1 0.6701 Test Accuracy du modele 2 0.6649746298789978
Nous observons que les test accuracy sont équivalents. Cela indique que l'équilibre des classes et l'augmentation des données n'apportent pas d'amélioration significative.
plot_model_curves(models_history, 'modele_brain_tumor_20241117_205951')
plot_model_curves(models_history, 'modele_brain_tumor_20241210_123938')
print('Test Accuracy du modele 1')
print('')
print(get_test_accuracy(models_history, 'modele_brain_tumor_20241117_205951'))
print('')
print('Test Accuracy du modele 3')
print('')
print(get_test_accuracy(models_history, 'modele_brain_tumor_20241210_123938'))
Test Accuracy du modele 1 0.6701 Test Accuracy du modele 3 0.2538
Nous constatons que les test accuracy varient considérablement, avec le modèle 3 affichant des performances nettement inférieures. Une explication détaillée sera fournie dans le rapport pour analyser cette différence.
En utilisant les métriques définies au début ✅
#--accuracy--
accuracy_1 = get_test_accuracy(models_history, 'modele_brain_tumor_20241117_205951')
accuracy_2 = get_test_accuracy(models_history, 'modele_brain_tumor_20241125_002219')
accuracy_3 = get_test_accuracy(models_history, 'modele_brain_tumor_20241210_123938')
#--loss--
loss_1 = get_test_loss(models_history, 'modele_brain_tumor_20241117_205951')
loss_2 = get_test_loss(models_history, 'modele_brain_tumor_20241125_002219')
loss_3 = get_test_loss(models_history, 'modele_brain_tumor_20241210_123938')
accuracy = [accuracy_1,accuracy_2,accuracy_3]
labels = [
'Model 1',
'Model 2',
'Model 3'
]
plot_accuracy_histogram(accuracy, labels)
model_1_metric = classification_report_aggregated(model_1, x_test, y_test)
model_2_metric = classification_report_aggregated(model_2, x_test, y_test)
model_3_metric = classification_report_aggregated(model_3, x_test, y_test)
13/13 [==============================] - 30s 2s/step 13/13 [==============================] - 39s 3s/step 13/13 [==============================] - 47s 4s/step
/Users/lilya/anaconda3/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1469: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior. /Users/lilya/anaconda3/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1469: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior. /Users/lilya/anaconda3/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1469: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.
df= pd.DataFrame()
df['Models'] = [f'model_{i}' for i in range(1,4)]
df['Precision'] = [model_1_metric['precision_avg'],model_2_metric['precision_avg'],model_3_metric['precision_avg']]
df['F1-score'] = [model_1_metric['f1_score_avg'],model_2_metric['f1_score_avg'],model_3_metric['f1_score_avg']]
df['Recall'] = [model_1_metric['recall_avg'],model_2_metric['recall_avg'],model_3_metric['recall_avg']]
df['Accuracy'] = [accuracy_1,accuracy_2,accuracy_3]
df['Loss'] = [loss_1,loss_2,loss_3]
df
| Models | Precision | F1-score | Recall | Accuracy | Loss | |
|---|---|---|---|---|---|---|
| 0 | model_1 | 0.724620 | 0.623519 | 0.670051 | 0.670100 | 4.159100 |
| 1 | model_2 | 0.712068 | 0.623227 | 0.664975 | 0.664975 | 4.787893 |
| 2 | model_3 | 0.064418 | 0.102756 | 0.253807 | 0.253800 | 1.429900 |
df_melted = df.melt(id_vars='Models', var_name='Metrics', value_name='Values')
fig = px.bar(
df_melted,
x='Metrics',
y='Values',
color='Models',
barmode='group',
title="Performance Metrics by Model",
labels={"Values": "Scores"}
)
fig.show()
Le graphique présente une comparaison des performances de trois modèles (model_1, model_2, model_3) à l’aide des métriques suivantes : Précision (Precision), F1-score, Rappel (Recall), Exactitude (Accuracy), et Perte (Loss).
Précision, F1-score, Rappel et Exactitude :
model_1 et model_2 montrent des performances similaires et élevées sur ces métriques. Cela indique une bonne capacité à prédire correctement les classes cibles, bien qu'elles s soient pas excellente. model_3, en revanche, affiche des scores nettement plus faibles, trés mauvais.Perte (Loss) :
model_2 a une perte (loss) nettement plus élevée que les autres, ce qui pourrait indiquer un problème d’optimisation ou un surapprentissage (overfitting , peut-être dû au fait que l'augmentation de données n'a été faite que dans le but d'équilibrer les classes et donc de montrer de nombreuses fois la même image légèrement modifiée dans les classes sous-représentées.)model_3 ait la plus faible perte, ses faibles performances sur les autres métriques montrent qu’une faible perte ne garantit pas une bonne généralisation.model_1 est le plus équilibré, combinant de bonnes performances sur toutes les métriques. Il est donc préférable.model_2 nécessite une attention particulière pour résoudre son problème de perte élevée, l'augmentation de données n'étant pas plus efficace.model_3 n'est pas meilleurs que de choisir au hasard une classe pour une image donnée.Ce notebook est à mi-chemin entre la présentation des résultats et une étude de quelques paramètres.
En raison d'un manque de ressources, nous nous sommes limités à 6 epochs et avons évité d'utiliser des architectures trop lourdes.
Dans le rapport final, nous détaillerons comment, idéalement, nous aurions calibré notre modèle pour obtenir une meilleure accuracy, selon nos recherches et analyses.